/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

#ifndef CORE_UTILS_DATE_H_
#define CORE_UTILS_DATE_H_

#include <time.h>
#include <string.h>

#include <string>

namespace aliyun {

namespace utils {

// depends on time.h, no guarantee to ensure thread safety
class Date {
 public:

  const static int kNumMonth = 12;
  static const char* kMonthName[];

  // default constructor, invalid date.
  Date()
      : time_(0) {
    ::memset(&tm_, 0, sizeof(struct tm));
  }

  // for local date time, use Date(time(NULL))
  Date(time_t t)
      : time_(t) {
    ::memcpy(&tm_, ::localtime(&time_), sizeof(struct tm));
  }

  // construct from `struct tm`
  Date(struct tm* tm) {
    tm_ = *tm;
    time_ = ::mktime(&tm_);
  }

  // construct by actual date time
  Date(int year, int mon, int day, int hour, int min, int sec) {
    //  care follow members of `struct tm`:
    //    tm_year   The number of years since 1900.
    //    tm_mon    The number of months since January, in the range 0 to 11.
    //    tm_wday   The number of days since Sunday, in the range 0 to 6.
    tm_.tm_year = year - 1900;
    tm_.tm_mon = mon - 1;
    tm_.tm_mday = day;
    tm_.tm_hour = hour;
    tm_.tm_min = min;
    tm_.tm_sec = sec;
    time_ = ::mktime(&tm_);
  }

  // get current local date
  static Date currentLocalDate() {
    Date d;
    d.time_ = time(NULL);
    d.tm_ = *::localtime(&d.time_);
    return d;
  }

  // get current UTC date
  static Date currentUtcDate() {
    Date d;
    d.time_ = time(NULL);
    d.tm_ = *::gmtime(&d.time_);
    return d;
  }

  bool invalid() const {
    return time_ == 0;
  }

#define IMPLEMENT_COMPARE(op) \
  bool operator op (const Date& rhs) const { \
    return time_ op rhs.time_; \
  }

  IMPLEMENT_COMPARE(==)
  IMPLEMENT_COMPARE(!=)
  IMPLEMENT_COMPARE(<=)
  IMPLEMENT_COMPARE(>=)
  IMPLEMENT_COMPARE(<)
  IMPLEMENT_COMPARE(>)

#undef IMPLEMENT_COMPARE

  // to human readable local time string, format: yyyy-mm-dd HH:MM:SS
  std::string toString() const {
    return format("%Y-%m-%d %H:%M:%S");
  }

  std::string format(const char* fmt) const {
    return format(fmt, &tm_);
  }

  // return raw handle
  const struct tm* c_tm() const {
    return &tm_;
  }
 private:

  static std::string format(const char* fmt, const struct tm* t) {
    char buffer[128];
    ::strftime(buffer, sizeof(buffer), fmt, t);
    return buffer;
  }

  time_t time_;
  struct tm tm_;  // cache local time represent.
};

// TODO(xu): move to implements file(.cc)
const char* Date::kMonthName[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };

}  // namespace utils

}  // namespace aliyun

#endif  // CORE_UTILS_DATE_H_
